home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
ulib
/
parse.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
11KB
|
566 lines
/***********************************************************************
* $Id: parse.c,v 0.80 1994/02/24 09:48:11 zhao Exp $
*
*. Copyright(c) 1993,1994 by T.C. Zhao
* All rights reserved.
*.
*
* A recursive descent parser that parses the following grammar:
*
* E -> E [+ -] T
* T -> T [* /] P'
* P' -> P | P^P
* P -> number | name | (E) | f(P) | -P | name = E
*
* note that function call is parsed as f(P) -> f((E))
*
* This grammar and implementation are correct and do
* not suffer the evaluation order problems an naive
* one does. In this parsar, a+b-c is evaluated as (a+b)-c.
*
* Note this is a minimum implementation I took out from one
* of the calculator programs I wrote a while ago. name = E
* is just for those who likes y = sin(x) more than sin(x).
*
* rd_evaluate simply evaluates an expresion (repeatedly if more
* variable values). An obvious optimation is to record the
* parse tree to avoid repeated parsing
*
***********************************************************************/
#if !defined (lint) && defined(F_ID)
char *id_rdp = "$Id: parse.c,v 0.80 1994/02/24 09:48:11 zhao Exp $";
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#if defined( __STRICT_ANSI__) || defined(__STDC__)
extern double j0(double), j1(double);
extern double y0(double), y1(double);
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef HUGE
#define HUGE 3.40282347e+38 /* max decimal value of a "float" */
#endif
#include "ulib.h"
typedef double (*d1d_f) (double); /* supported func types */
#define TOKEN_DBG 0
/*
* We assign every token an ASCII value for easy debug printing
*/
typedef enum
{
NAME = 'n', NUMBER = 'd', END = 'e', FUNC = 'f',
PLUS = '+', MINUS = '-', MUL = '*', DIV = '/', POW = '^',
ASSIGN = '=',
LP = '(', RP = ')'
}
Tokens;
/***** Symbol tables *********/
typedef struct symtab_
{
struct symtab_ *next;
char *str;
double val;
}
Symtab;
/***** Local functions *****/
static double term(void), primary(void);
static Symtab *lookup(const char *, int), *insert(const char *);
static Tokens get_token(void);
static int nerr;
static Tokens current_token;
static char *current_name = "x";
static double current_number;
static d1d_f search_func(const char *), current_func;
/* a simple error message emitter */
static int eout;
static void
rdp_error(const char *s)
{
if (eout)
fputs(s, stderr);
nerr++;
}
static double
expression(void)
{
double left = term();
while (1)
switch (current_token)
{
case PLUS:
get_token();
left += term();
break;
case MINUS:
get_token();
left += term();
break;
default:
return left;
}
}
static double pp(void); /* hack to get power ^ or ** to work */
static double
term(void)
{
double left = pp();
double d;
while (1)
switch (current_token)
{
case MUL:
get_token();
left *= pp();
break;
case DIV:
get_token();
if ((d = pp()) == 0)
{
rdp_error("Division by 0\n");
d = 1.e-6;
}
left /= d;
break;
default:
return left;
}
}
static double
pp(void)
{
double left = primary();
while (1)
switch (current_token)
{
case POW:
get_token();
left = pow(left, primary());
break;
default:
return left;
}
}
static double
primary(void)
{
double e;
d1d_f thisf;
switch (current_token)
{
case NUMBER:
get_token();
return current_number;
case FUNC:
get_token();
thisf = current_func;
e = primary();
return thisf(e);
case NAME:
if (get_token() == ASSIGN)
{
Symtab *n = insert(current_name);
get_token();
n->val = expression();
return n->val;
}
/*
* un-used variable will have a value of zero and will be entered
* into sumbol table, but an error will set
*/
if (lookup(current_name, 0) == 0)
nerr++;
return lookup(current_name, 1)->val;
case MINUS:
get_token();
return -primary();
case LP:
get_token();
e = expression();
if (current_token != RP)
{ /* ( for VI */
rdp_error(" ) expected\n");
return -1;
}
get_token(); /* eat rp */
return e;
case END:
return 1;
default:
fprintf(stderr, "bad token\n");
return 1;
}
}
/********* Tokenizer **********************/
static char strexp[1024]; /* current expression */
static char *current;
static Tokens
check_str(void)
{
static char str[512];
char *p = str;
d1d_f cf;
do
{
*p++ = *current++;
}
while (isalnum(*current));
*p = '\0';
/* maybe two case: a variable name, and a function name */
current_name = str;
if ((cf = search_func(str)))
current_func = cf;
return cf ? FUNC : NAME;
}
/* get a number, very lenient */
static Tokens
check_num(void)
{
static char str[1024];
char *p = str;
do
{
*p++ = *current++;
}
while (isdigit(*current) || *current == '.');
*p = '\0';
current_number = atof(str);
return NUMBER;
}
/* skil all spaces and return the first no-space character */
static int
skip_space(void)
{
/* skip all spaces */
while (*current && isspace(*current))
{
current++;
}
return *current;
}
static Tokens
get_token(void)
{
Tokens thistoken;
if (!skip_space())
return current_token = END;
switch (*current)
{
case '=':
case '/':
case '+':
case '-':
case '^':
case '(':
case ')':
thistoken = *current++;
break;
case '*': /* check for ** */
current++;
if (*current == '*')
{
++current;
thistoken = POW;
}
else
{
thistoken = MUL;
}
break;
default:
if (isalpha(*current))
thistoken = check_str();
else if (isdigit(*current) || *current == '.')
thistoken = check_num();
else
{
rdp_error("bad token\n");
thistoken = END;
}
break;
}
#if TOKEN_DBG
fprintf(stderr, "token: %c Str: %s Num: %g\n",
thistoken, current_name, current_number);
#endif
return current_token = thistoken;
}
static void
rd_parser_init(void)
{
static initok;
if (!initok)
{
insert("pi")->val = acos(-1.0);
insert("e")->val = exp(1.0);
initok = 1;
}
}
/*****************************************************************
* Global entry point.
*
* Function returns non-zero for error
******************************************************************/
int
rd_evaluate(const char *mexp, float *x, float *y, int n)
{
int i;
double e;
if (!mexp || !*mexp)
return -1;
rd_parser_init();
nerr = 0;
current = strcpy(strexp, mexp);
for (i = 0; i < n && !nerr; i++)
{
current = strexp;
get_token();
lookup("x", 1)->val = x[i];
lookup("X", 1)->val = x[i]; /* just in case */
e = expression();
if (!nerr)
y[i] = e;
}
return nerr;
}
/***************************************************************
* Symbol table
****************************************************************/
#include <malloc.h>
#define TABSIZE 31
static Symtab *table[TABSIZE];
static Symtab *
lookup(const char *s, int in)
{
int ii = 0;
Symtab *st;
const char *p = s;
while (*p)
ii = ii << 1 ^ *p++;
if (ii < 0)
ii = -ii;
ii %= TABSIZE;
for (st = table[ii]; st; st = st->next)
if (strcmp(st->str, s) == 0)
return st;
/* name not found, see if need to insert intothe symbol table */
if (in)
{
Symtab *n = (Symtab *) malloc(sizeof(*n));
n->str = strdup(s);
n->val = 0.0;
n->next = table[ii];
table[ii] = n;
return n;
}
return 0;
}
static Symtab *
insert(const char *s)
{
return lookup(s, 1);
}
/*************************************************************
* All supported function takes 1 double, and returns double
**************************************************************/
extern double cbrt(double);
/*** Custom functions **/
static double
deg(double d)
{
return (d * 180.0 / M_PI);
}
static double
rad(double r)
{
return (r * M_PI / 180.0);
}
/*******************************************************************
* Structure holding all supported functions.
* It would be nice to be able to check the validity of the
* arguments
*******************************************************************/
typedef struct
{
const char *fname; /* function name */
d1d_f mfunc; /* the function */
double lo, hi; /* valid argument range */
}
d1d_t;
/*
* all double functions taking 1 double argument. if hi==lo,
* any argument is ok
*/
static d1d_t d1d[] =
{
/* trig functions */
{"sin", sin, 0.0, 0.0},
{"cos", cos, 0.0, 0.0},
{"tan", tan, 0.0, 0.0},
{"acos", acos, -1.0, 1.0},
{"asin", asin, -1.0, 1.0},
{"atan", atan, 0.0, 0.0},
/* roots, power etc */
{"sqrt", sqrt, 0.0, HUGE},
{"cbrt", cbrt, 0.0, 0.0},
{"abs", fabs, 0.0, 0.0},
/* exponetial and log */
{"exp", exp, 0.0, 0.0},
{"log", log, 0.0, HUGE},
{"log10", log, 0.0, HUGE},
/* misc functions */
{"deg", deg, 0.0, 0.0},
{"rad", rad, 0.0, 0.0},
/* special functions */
{"j0", j0,},
{"j1", j1},
{"y0", y0},
{"y1", y1}
};
static d1d_f
search_func(const char *s)
{
register d1d_t *dd = d1d + sizeof(d1d) / sizeof(d1d[0]);
while (--dd >= d1d && strcmp(s, dd->fname))
;
return (dd < d1d) ? 0 : dd->mfunc;
}
#if 0 /* not used in bit */
void
list_builtin_functions(void)
{
d1d_t *dd = d1d + sizeof(d1d) / sizeof(d1d[0]);
int n = 0;
fprintf(stderr, "supported functions\n");
while (--dd >= d1d)
{
printf("%7s ", dd->fname);
if ((++n % 7) == 0)
putc('\n', stdout);
}
putc('\n', stdout);
}
/* check if an argument is ok */
int
valid_arg(register d1d_f f, register double arg)
{
register d1d_t *dd = d1d + sizeof(d1d) / sizeof(d1d[0]);
while (--dd >= d1d && dd->mfunc != f)
;
return (dd->lo == dd->hi) ||
(arg > dd->lo && arg < dd->hi);
}
#endif
#ifdef TEST
main()
{
char in[1024];
static float x[2] =
{1.0, 2.0};
float y[2];
int i, status;
while (fgets(in, 1024, stdin))
{
i = strlen(in) - 1;
in[i] = '\0';
if ((status = rd_evaluate(in, x, y, 2)))
fprintf(stderr, "error parsing %s\n", in);
else
{
fprintf(stderr, "For %s\n", in);
fprintf(stderr, "At x=%f %s=%g\n", x[0], in, y[0]);
fprintf(stderr, "At x=%f %s=%g\n", x[1], in, y[1]);
}
}
}
#endif